//========================================================//
// Socket Lib                                             //
// Basic classes ans interfaces                           //
// Author: Bezdolnyj Alexey aka BAM  bamalex@ngs.ru       //
// Author: Krechetov Anton aka kae   kae@gorodok.net      //
// NSU FIT May-July 2004                                  //
//========================================================//


#ifndef _SOCKET_PROTOCOL_STACK_
#define _SOCKET_PROTOCOL_STACK_

//#include "C:\\Documents and Settings\\sfaktorovich\\My Documents\\remoteconsole2\\vld.h"

#include "osocklib.h"
#include "../common/packet.h"
#include "../common/cthreads.h"

namespace BAM
{
	using namespace std;

	static const SocketException EXC_BadPacket("Invalid Packet");
	static const SocketException EXC_BadOperation("Invalid operation with socket");
	static const SocketException EXC_Login("No such login");
	static const SocketException EXC_Auth("Authorization failed");
	static const SocketException EXC_Field("Invalid field value");
	static const SocketException EXC_QEmpty("Queue is empty");
	static const SocketException EXC_Sequence("Assembling routine is broken");

	static const SocketException EXC_Unsupported("Method unsupported");

	class iPacketController
	{
	public:
		virtual void onConnect(const cIPAddr &Addr) = 0;
		virtual void onDisconnect() = 0;
		virtual void onInit(const cIPAddr &Addr) = 0;
		virtual void onSend(const cPacket &P) = 0;
		virtual void onRecv(const cPacket &P) = 0;

		virtual bool haveOutcome() const {return false; }// = 0;
		
		virtual bool haveIncome() const {return false;}// = 0 /* fuck */

		virtual bool isInited() const = 0;
		virtual bool isFinished() const = 0;
		virtual Sock_State state(Sock_State TransportState) const {return SSTATE_DISCONNECTED;}//= 0;

		virtual cPacket outcome() = 0;
		virtual cPacket income() = 0;
		virtual iPacketController* clone() const = 0;

		virtual ~iPacketController(){};
	};

	class cPSocket : public iSocket
	{
	protected:
		
		iPacketController *vPControl;

	public:
		iSocket *vSock;
		cPSocket(iPacketController *PControler, iSocket *Transport=0);
		~cPSocket();
		void shortinit(SOCKET S, const cIPAddr &Addr);
		void shortinit(iSocket* s);
		void setTransport(iSocket* transport);
		iSocket* getTransport();

		void init();
		void connect(const cIPAddr &Addr);
		void disconnect();
		cPacket recv();
		void send(const cPacket &P);
		Sock_State state() const
		{
			return vPControl->state(vSock->state()); /* fuck again, pure virtual function call here */
		}
		const cIPAddr& getAddr() const
		{
			return vSock->getAddr();
		}
		bool isReadable();
		bool isWriteable();
		bool isAcceptable()
		{
			return false;
		}
		SOCKET descriptor()
		{
			return vSock->descriptor();
		}
		iSocket* clone() const;
	};

	class iPacketWrapper
	{
	public:
		virtual ~iPacketWrapper()
		{}

		virtual qword getIntField(dword Index) const = 0;
		virtual void setIntField(dword Index, qword Val) = 0;
		virtual dword getIntFieldCount() const = 0;

		virtual cPacket getBinField(dword Index) const = 0;
		virtual dword getBinFieldLength(dword Index) const = 0;
		virtual void setBinField(dword Index, const cPacket &P) = 0;
		virtual dword getBinFieldCount() const = 0;

		virtual dword getHeaderLength() const = 0;
		virtual dword getFullLength(const cPacket &P) const = 0;
		virtual dword getMaxLength() const = 0;

		virtual cPacket compile() = 0;
		virtual iPacketWrapper* decompile(const cPacket &P) = 0;//I assume it allocates memory in heap; call `delete' to free it
	};

	class cAssemblerController : virtual public iPacketController
	{
	private:
		word vFullLength;
		cPacket vBuffer;
		iPacketWrapper* vWrapper;
		bool vRunning;

	protected:
		void setWrapper(iPacketWrapper* s);
		virtual void received(const iPacketWrapper &P) = 0;

	public:
		cAssemblerController(iPacketWrapper* wr);
		virtual ~cAssemblerController();
		void onRecv(const cPacket &P);
		void reset();
	};

	class cActiveController : virtual public iPacketController
	{
	protected:
		cPSocket* vOwner;

	public:
		cActiveController();
		~cActiveController();
		void setOwner(cPSocket* owner);
		virtual void onIdle(Sock_State TransportState) = 0;
	};

	static const dword Idle_Time_Ms = 3000;
	static const SocketException EXC_WouldBlock("Operation cannot be completed immediately.");

	class cActiveSocket : public cPSocket
	{

	private:
	protected:
									//# it makes sense only in cActiveSocket::recv() when there is no packet to return
		bool m_isBlocking;			//# if it is true cActiveSocket::recv() keeps trying to receive a packet and blocks
									//# if it is false cActiveSocket::recv() throws EXC_WouldBlock

		class cSocketJob : public iRunnable
		{
		private:
			cActiveSocket* vSocket;

		public:
			cSocketJob(cActiveSocket* ctrl);
			void run();
			void stop();
		};

		mutable cRecursiveMutex vActiveLock;
		cTimer* vTimer;
		cSocketJob* vJob;

	public:
		cActiveSocket(cActiveController* control, iSocket* transport = 0);
		~cActiveSocket();
		void idle();
		void shortinit(SOCKET S, const cIPAddr &Addr);
		void shortinit(iSocket* s);
		void setTransport(iSocket* transport);
		iSocket* getTransport();
		void init();
		void connect(const cIPAddr &Addr);
		void disconnect();
		cPacket recv();
		void send(const cPacket &P);
		Sock_State state() const;
		const cIPAddr& getAddr() const;
		bool isReadable();
		bool isWriteable();
		bool isAcceptable();
		SOCKET descriptor();
		iSocket* clone() const;


		void setBlocking(bool value)
		{
			vActiveLock.lock();
			m_isBlocking = value;
			vActiveLock.unlock();
		}
		bool isBlocking() const
		{
			return m_isBlocking;
		}
	};
}
#endif

